#include "PluginManager.h"
#include "../DetourHelper.h"
#include "../Exception.h"
#include "../Interfaces/IEnvironment.h"


k_PluginManager::k_PluginManager()
	: mb_Initialized(false)
	, mr_ServiceLocator_(NULL)
{
}


k_PluginManager::~k_PluginManager()
{
}


void* k_PluginManager::QueryInterface(const GUID& ar_InterfaceGuid)
{
	if (ar_InterfaceGuid == __uuidof(IPluginManager)) 
		return static_cast<IPluginManager*>(this);
	else if (ar_InterfaceGuid == __uuidof(IService)) 
		return static_cast<IService*>(this);

	return NULL; 
}


void k_PluginManager::Initialize(IServiceLocator& ar_ServiceLocator)
{
	mr_ServiceLocator_ = &ar_ServiceLocator;
	mb_Initialized = true;
}


void k_PluginManager::Deinitialize()
{
	this->UnloadPlugins();
	mb_Initialized = false;
}


bool k_PluginManager::get_IsInitialized() const
{
	return mb_Initialized;
}


void k_PluginManager::LoadPlugins()
{
	IEnvironment& lr_Environment = object_cast<IEnvironment>(*mr_ServiceLocator_);
	std::wstring ls_LogFile = lr_Environment.get_WalhallPath()+L"Walhall.log";

	std::wstring ls_SearchPath = lr_Environment.get_WalhallPath() + L"Plugins\\*.dll";
	WIN32_FIND_DATAW lr_FileData;
	HANDLE lh_File = FindFirstFile(ls_SearchPath.c_str(), &lr_FileData);
	bool lb_Found = lh_File != (HANDLE)-1;
	while (lb_Found)
	{
		PrintLog(ls_LogFile, L"Loading plugin: " << lr_FileData.cFileName << L"\n");
		std::wstring ls_FileName = lr_Environment.get_WalhallPath() + L"Plugins\\" + lr_FileData.cFileName;
		this->RegisterPlugin(ls_FileName.c_str());
		lb_Found = FindNextFile(lh_File, &lr_FileData) == TRUE;
	}

	if (lh_File != NULL)
		FindClose(lh_File);
}


void k_PluginManager::UnloadPlugins()
{
	while (!mk_Plugins.empty())
		this->UnloadPlugin(mk_Plugins.front());

	mk_Plugins.clear();
	mk_PluginLibraries.clear();
}


std::vector<IDirect3D9BasePlugin*> k_PluginManager::get_Plugins() const
{
	return mk_Plugins;
}


void k_PluginManager::RegisterPlugin(const std::wstring as_DllFileName)
{ 
	IEnvironment& lr_Environment = object_cast<IEnvironment>(*mr_ServiceLocator_);
	std::wstring ls_LogFile = lr_Environment.get_ModulePath()+L"Walhall.log";

	HMODULE lh_Module = ::LoadLibrary(as_DllFileName.c_str());

    if (lh_Module != NULL)
    {
		typeCreatePlugin l_CreatePlugin_ = (typeCreatePlugin)::GetProcAddress(lh_Module, "CreatePlugin");
		
		if (l_CreatePlugin_ != NULL)
		{
			IDirect3D9BasePlugin* lr_Plugin_ = l_CreatePlugin_(*mr_ServiceLocator_);
			if (lr_Plugin_ != NULL)
			{
				PrintLog(ls_LogFile, L"DLL: Created plugin data.\n");

				this->AddPlugin(lr_Plugin_, lh_Module);

				PrintLog(ls_LogFile, L"DLL: Registered plugin: " << as_DllFileName << "\n");
			}
			else
			{
				PrintLog(ls_LogFile, L"DLL: Plugin has not been created!\n");
			}
		}
		else
		{
			PrintLog(ls_LogFile, L"DLL: Plugin has not been created!\n");
		}
	}
	else
		PrintLog(ls_LogFile, L"DLL: Failed registering plugin: " << as_DllFileName << "\n");
}


void k_PluginManager::AddPlugin(IDirect3D9BasePlugin* ar_Plugin_, HINSTANCE ah_Library)
{
	mk_PluginLibraries.push_back(ah_Library);
	mk_Plugins.push_back(ar_Plugin_);
}


void k_PluginManager::UnloadPlugin(IDirect3D9BasePlugin* ar_Plugin_)
{
	IEnvironment& lr_Environment = object_cast<IEnvironment>(*mr_ServiceLocator_);
	std::wstring ls_LogFile = lr_Environment.get_ModulePath()+L"Walhall.log";

	for (unsigned int i = 0; i < mk_Plugins.size(); ++i)
		if (mk_Plugins[i] == ar_Plugin_)
		{
			try
			{
				if (mk_Plugins[i]->IsInitialized())
					mk_Plugins[i]->Deinitialize();
			}
			catch (Exception& e)
			{
				PrintLog(ls_LogFile, L"Fatal Error: Deinitialize of " << mk_Plugins[i]->get_Name() << L" failed. \n");
				PrintLog(ls_LogFile, L"\t" << e.GetMessage() << L" failed. \n");
			}

			delete mk_Plugins[i];
			::FreeLibrary(mk_PluginLibraries[i]);

			mk_PluginLibraries.erase(mk_PluginLibraries.begin() + i);
			mk_Plugins.erase(mk_Plugins.begin() + i);

			return;
		}
}
